home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / RTF / RichText.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  24KB  |  967 lines

  1. /* $Header: /usr/people/pcd/Src/RTF/RCS/RichText.c,v 1.1 92/11/23 12:58:54 pcd Exp Locker: pcd $
  2.  * from: SCCSID[] = "OSF/Motif: @(#)DrawingA.c    3.19 91/01/10";
  3.  */
  4.  
  5. #include "RichTextP.h"
  6.  
  7. #include <X11/Xatom.h>
  8. #include "debug.h"
  9.  
  10. /* Internal procedures:
  11. */
  12. static void             ClassPartInitialize( WidgetClass class);
  13. static void             Initialize( XcRichTextWidget request,
  14.                    XcRichTextWidget new);
  15. static void             Redisplay( XcRichTextWidget da, XEvent * event,
  16.                   Region region);
  17. static void             Resize( XcRichTextWidget da);
  18. static void             Destroy( XcRichTextWidget da);
  19. static Boolean          SetValues( XcRichTextWidget current,
  20.                   XcRichTextWidget request,
  21.                   XcRichTextWidget new);
  22. static void             Realize(Widget w, Mask *valueMask,
  23.                 XSetWindowAttributes *attributes);
  24. static int
  25. InvertSelection(XcRichTextWidget w);
  26. static int
  27. InvertDifference(XcRichTextWidget w,
  28.          int rx, int ry,
  29.          unsigned rw, unsigned rh);
  30.  
  31. /* Default translation table and action list */
  32. /*@@ button 2 for link action? */
  33. /*@@ double click stuff? */
  34. static char defaultTranslations[] =
  35. "<Btn1Down>:     select-start() \n\
  36. <Btn1Motion>:   extend-adjust() \n\
  37. <Btn1Up>:       extend-end(PRIMARY, CUT_BUFFER0) MotionVerify()\n\
  38. <Btn3Down>:     extend-start() \n\
  39. <Btn3Motion>:   extend-adjust() \n\
  40. <Btn3Up>:       extend-end(PRIMARY, CUT_BUFFER0) \
  41. ";
  42.  
  43. /*  Resource definitions for RichText
  44.  */
  45.  
  46. static XtResource resources[] =
  47. {
  48.   {
  49.     XmNfontList,
  50.     XmCFontList, XmRFontList, sizeof (XmFontList),
  51.     XtOffset (XcRichTextWidget, rich_text.fontList),
  52.     XmRImmediate, (caddr_t) NULL,
  53.   },
  54.   {
  55.     XmNmotionVerifyCallback,
  56.     XmCCallback, XmRCallback, sizeof (XtCallbackList),
  57.     XtOffset (XcRichTextWidget, rich_text.motion_verify_callback),
  58.     XmRImmediate, (caddr_t) NULL,
  59.   },
  60.   {
  61.     XmNmarginHeight, 
  62.     XmCMarginHeight, 
  63.     XmRVerticalDimension, 
  64.     sizeof (Dimension),
  65.     XtOffset (XcRichTextWidget, rich_text.margin_height),
  66.     XmRImmediate,
  67.     (caddr_t) 10,
  68.   },
  69.   {
  70.     XmNmarginWidth, 
  71.     XmCMarginWidth, 
  72.     XmRHorizontalDimension, 
  73.     sizeof (Dimension),
  74.     XtOffset (XcRichTextWidget, rich_text.margin_width), 
  75.     XmRImmediate,
  76.     (caddr_t) 10,
  77.   },
  78.   {
  79.     XcNrtf,
  80.     XcCRtf, XmRBoolean, sizeof (Boolean),
  81.     XtOffset (XcRichTextWidget, rich_text.rtf),
  82.     XmRImmediate, (caddr_t) 0,
  83.   },
  84.   {
  85.     XmNvalue,
  86.     XmCValue, XmRString, sizeof (String),
  87.     XtOffset (XcRichTextWidget, rich_text.value),
  88.     XmRString, (caddr_t) NULL,
  89.   },
  90. };
  91.  
  92.  
  93. static void MotionVerify(Widget, XEvent*, String*, Cardinal*);
  94. static void SelectStart(Widget, XEvent*, String*, Cardinal*);
  95. static void ExtendStart(Widget, XEvent*, String*, Cardinal*);
  96. static void ExtendEnd(Widget, XEvent*, String*, Cardinal*);
  97. static void ExtendAdjust(Widget, XEvent*, String*, Cardinal*);
  98.  
  99. static XtActionsRec actionsList[] =
  100. {
  101.    { "enter",    (XtActionProc) _XmPrimitiveEnter },
  102.    { "leave",    (XtActionProc) _XmPrimitiveLeave },
  103.    { "Help",     (XtActionProc) _XmPrimitiveHelp },       /* Motif 1.0 */
  104.  
  105.    { "select-start",  (XtActionProc) SelectStart },
  106.    { "extend-start",  (XtActionProc) ExtendStart },
  107.    { "extend-adjust", (XtActionProc) ExtendAdjust },
  108.    { "extend-end",    (XtActionProc) ExtendEnd },
  109.    { "MotionVerify", (XtActionProc) MotionVerify }
  110. };
  111.  
  112.  
  113. /****************************************************************
  114.  *
  115.  * Full class record constant
  116.  *
  117.  ****************************************************************/
  118.  
  119. externaldef( xcrichtextclassrec) XcRichTextClassRec
  120.        xcRichTextClassRec =
  121. {
  122.   {            /* core_class fields      */
  123.     (WidgetClass) &xmPrimitiveClassRec,    /* superclass         */
  124.     "XcRichText",                /* class_name         */
  125.     sizeof(XcRichTextRec),            /* widget_size        */
  126.     NULL,                    /* class_initialize   */
  127.     ClassPartInitialize,            /* class_part_init    */
  128.     FALSE,                    /* class_inited       */
  129.     (XtInitProc) Initialize,            /* initialize         */
  130.     NULL,                    /* initialize_hook    */
  131.     Realize,                       /* realize            */
  132.     actionsList,                /* actions          */
  133.     XtNumber(actionsList),            /* num_actions          */
  134.     resources,                        /* resources          */
  135.     XtNumber(resources),            /* num_resources      */
  136.     NULLQUARK,                        /* xrm_class          */
  137.     TRUE,                    /* compress_motion    */
  138.     XtExposeCompressMaximal,            /* compress_exposure  */
  139.     TRUE,                    /* compress_enterlv   */
  140.     FALSE,                    /* visible_interest   */
  141.     (XtWidgetProc)Destroy,                    /* destroy            */
  142.     (XtWidgetProc) Resize,            /* resize             */
  143.     (XtExposeProc) Redisplay,            /* expose             */
  144.     (XtSetValuesFunc) SetValues,        /* set_values         */
  145.     NULL,                    /* set_values_hook    */
  146.     (XtAlmostProc) _XtInherit,            /* set_values_almost  */
  147.     NULL,                    /* get_values_hook    */
  148.     NULL,                    /* accept_focus       */
  149.     XtVersion,                    /* version            */
  150.     NULL,                    /* callback_private   */
  151.     defaultTranslations,            /* tm_table           */
  152.     NULL,                    /* query_geometry     */
  153.     NULL,                                     /* display_accelerator   */
  154.     NULL,                                /* extension             */
  155.   },
  156.   
  157.   {        /* composite_class fields */
  158.     (XtWidgetProc) _XtInherit,        /* primitive border highlight   */
  159.     (XtWidgetProc) _XtInherit,        /* primitive border unhighlight   */
  160.     NULL,         /* translations                 */
  161.     NULL,         /* arm_and_activate             */
  162.     NULL,         /* get resources                */
  163.     0,            /* num get_resources            */
  164.     NULL,         /* extension                    */
  165.   },
  166.   
  167. };
  168.  
  169. externaldef( xmrichtextwidgetclass) WidgetClass xcRichTextWidgetClass
  170.                                        = (WidgetClass) &xcRichTextClassRec ;
  171.  
  172.  
  173. /****************************************************************/
  174. static void
  175. ClassPartInitialize(
  176.             WidgetClass    class)
  177. {   
  178.   
  179.   _XmFastSubclassInit( class, XmPRIMITIVE_BIT) ;
  180.  
  181.   return ;
  182. }
  183.  
  184.  
  185. /****************************************************************/
  186. static void
  187. CopyFontList(XcRichTextWidget new)
  188. {
  189.   if (new->rich_text.fontList == NULL){
  190.     XFontStruct  *fs;
  191.     XmStringCharSet cset = (XmStringCharSet) XmSTRING_DEFAULT_CHARSET;
  192.     
  193.     fs = XLoadQueryFont(XtDisplay(new), "fixed");
  194.     new->rich_text.fontList = XmFontListCreate (fs, cset);
  195.   }
  196.   /* Make a local copy of the font list */
  197.   else{
  198.     new->rich_text.fontList = XmFontListCopy(new->rich_text.fontList);
  199.   }
  200. }
  201.  
  202. static void
  203. Initialize(
  204.             XcRichTextWidget request,
  205.             XcRichTextWidget new)
  206. /****************
  207.  * Ensure that the width and height are not 0.
  208.  ****************/
  209. {
  210. /****************/
  211.   Display* disp = XtDisplay(new);
  212.   void* flow;
  213.   int f;
  214.  
  215.   CopyFontList(new);
  216.  
  217.   new->rich_text.value = XtNewString(new->rich_text.value);
  218.   new->rich_text.bytes = new->rich_text.value ? strlen(new->rich_text.value):0;
  219.   new->rich_text.point = new->rich_text.mark = 0; /* no selection */
  220.   new->rich_text.invert = 0; 
  221.  
  222.   new->rich_text.flow = flow = XTextView_create(XtDisplay(new),
  223.                         (void*)new);
  224.   
  225.   Resize(new);
  226.  
  227. #if 0 
  228. /* FINDME  - This codes forces you to have text in the widget prior to
  229.    setting a minimum height.  Unfortunately, it doesn't reset the height
  230.    properly, when you do set text there ... until you reset the 
  231.    XmNheight resource. */
  232.  
  233.   if(XTextView_string(flow, new->rich_text.value, new->rich_text.rtf)){
  234.     /* find out how tall flow is after formatting. */
  235.     new->core.height = XTextView_height(flow) 
  236.       + 2 * new->rich_text.margin_height;
  237.     /* @@ not all text may fit! */
  238.   }
  239. #endif
  240.   return ;
  241. }
  242.  
  243. /****************************************************************/
  244. static void
  245. Destroy(
  246.     XcRichTextWidget new)
  247. {
  248. /****************/
  249.   int f;
  250.  
  251.   if(new->rich_text.value) XtFree(new->rich_text.value);
  252.   if(new->rich_text.fontList) XmFontListFree(new->rich_text.fontList);
  253.  
  254.   if(new->rich_text.flow)
  255.     XTextView_destroy(new->rich_text.flow);
  256. }
  257.  
  258. /****************************************************************/
  259. static void
  260. Redisplay(
  261.             XcRichTextWidget da,
  262.             XEvent * event,
  263.             Region region)
  264. /****************
  265.  * General redisplay function called on exposure events.
  266.  ****************/
  267. {
  268.   XcRichTextPart* p = &da->rich_text;
  269.   long s, e;
  270.   int flicker;
  271.   int* x = da->rich_text.sx;
  272.   int* y = da->rich_text.sy;
  273.  
  274.   if(p->point < p->mark){
  275.     s = p->point;
  276.     e = p->mark;
  277.   }else{
  278.     s = p->mark;
  279.     e = p->point;
  280.   }
  281.  
  282.   flicker = s != e 
  283.     && y[0] <= event->xexpose.y+event->xexpose.height
  284.       && y[3] >= event->xexpose.y;
  285.  
  286.   if(flicker){
  287.     XClearArea(XtDisplay((Widget)da), XtWindow((Widget)da),
  288.            event->xexpose.x,
  289.            event->xexpose.y,
  290.            event->xexpose.width,
  291.            event->xexpose.height,
  292.            0);
  293.     
  294.     InvertDifference(da,
  295.              event->xexpose.x,
  296.              event->xexpose.y,
  297.              event->xexpose.width,
  298.              event->xexpose.height);
  299.   }
  300.  
  301.   XTextView_render(da->rich_text.flow,
  302.            event->xexpose.x,
  303.            event->xexpose.y,
  304.            event->xexpose.width,
  305.            event->xexpose.height);
  306.  
  307.   if(flicker)
  308.     InvertSelection(da);
  309. }
  310. /****************************************************************/
  311. static void
  312. Resize(
  313.             XcRichTextWidget da)
  314. /****************
  315.  * resize the XFlow
  316.  ****************/
  317. {
  318.   XTextView_resize(da->rich_text.flow,
  319.            da->rich_text.margin_width,
  320.            da->rich_text.margin_height,
  321.            da->core.width - da->rich_text.margin_width*2,
  322.            da->core.height - da->rich_text.margin_height*2);
  323.   
  324.   return ;
  325. }
  326.  
  327. /****************************************************************/
  328. static void Realize(w, p_valueMask, attributes)
  329. Widget w;
  330. Mask *p_valueMask;
  331. XSetWindowAttributes *attributes;
  332. {
  333.   Mask valueMask = *p_valueMask;
  334.   
  335.   valueMask |= CWBitGravity | CWDontPropagate;
  336.   attributes->bit_gravity = NorthWestGravity;
  337.   attributes->do_not_propagate_mask =
  338.     ButtonPressMask | ButtonReleaseMask |
  339.       KeyPressMask | KeyReleaseMask | PointerMotionMask;
  340.   
  341.   XtCreateWindow (w, InputOutput, CopyFromParent, valueMask, attributes);
  342.   
  343.   XTextView_drawable(((XcRichTextWidget)w)->rich_text.flow,
  344.              XtWindow(w),
  345.              ((XcRichTextWidget)w)->core.depth); 
  346. }
  347.  
  348. /****************************************************************/
  349. static Boolean
  350. SetValues(
  351.             XcRichTextWidget current,
  352.             XcRichTextWidget request,
  353.             XcRichTextWidget new)
  354. /****************
  355.  * change font, string, etc.
  356.  ****************/
  357. {
  358. /****************/
  359.   Boolean redisplay;
  360.   void* flow = new->rich_text.flow;
  361.   Boolean new_height = 0, new_string = 0;
  362.   int f;
  363.  
  364.   /** This routine should only reformat the text once! **/
  365.  
  366.   if(new->rich_text.value != current->rich_text.value){
  367.     XtFree(current->rich_text.value);
  368.     new_string = 1;
  369.     new->rich_text.value = XtNewString(new->rich_text.value);
  370.     new->rich_text.bytes = new->rich_text.value ? strlen(new->rich_text.value):0;
  371.   }
  372.  
  373.   if(new->rich_text.fontList != current->rich_text.fontList){
  374.     CopyFontList(new);
  375.     new_string = 1;
  376.   }
  377.  
  378.   if(new_string || new->rich_text.rtf != current->rich_text.rtf){
  379.     if(XTextView_string(flow,
  380.             new->rich_text.value,
  381.             new->rich_text.rtf))
  382.       new_height = 1;
  383.     redisplay = 1;
  384.     new->rich_text.point = new->rich_text.mark = 0;
  385.   }
  386.  
  387.   if(new->core.width != current->core.width){
  388.     new_height = redisplay = 1;
  389.   }
  390.  
  391.   if(new_height
  392.      || new->rich_text.margin_width != current->rich_text.margin_width
  393.      || new->rich_text.margin_height != current->rich_text.margin_height)
  394.     {
  395.       XTextView_resize(flow,
  396.                new->rich_text.margin_width,
  397.                new->rich_text.margin_height,
  398.                new->core.width - new->rich_text.margin_width*2,
  399.                32000); /* @# */
  400.       new->core.height = XTextView_height(flow) +
  401.     2 * new->rich_text.margin_height;
  402.       redisplay = 1;
  403.     }
  404.  
  405.   return( redisplay ) ;
  406. }
  407.  
  408. /****************************************************************/
  409. Widget
  410. XcCreateRichText(
  411.             Widget          p,          /*  parent widget    */
  412.             String          name,    /*  widget name        */
  413.             ArgList         args,    /*  arg list    */
  414.             Cardinal        n)          /*  arg count    */
  415. /****************
  416.  * This function creates and returns a Rich Text widget.
  417.  ****************/
  418. {
  419. /****************/
  420.  
  421.     return( XtCreateWidget( name, xcRichTextWidgetClass, p, args, n)) ;
  422.     }
  423.  
  424.  
  425. static void
  426. MotionVerify(Widget w, XEvent* ev, String* dummy, Cardinal* unused)
  427. {
  428.   XcRichTextWidget rt = (XcRichTextWidget)w;
  429.   XcRichTextCallbackStruct cb;
  430.   cb.reason = XmCR_INPUT;
  431.   cb.event = ev;
  432.   cb.window = XtWindow(rt);
  433.  
  434.   cb.mark = rt->rich_text.mark;
  435.   cb.point = rt->rich_text.point;
  436.   cb.value = rt->rich_text.value;
  437.  
  438.   XtCallCallbackList ((Widget) rt,
  439.               rt->rich_text.motion_verify_callback, (XtPointer) &cb) ;
  440. }
  441.  
  442. static void
  443. PrepareInvertGC(XcRichTextWidget w)
  444. {
  445.   XGCValues values;
  446.   unsigned long valuemask = GCFunction | GCForeground;
  447.  
  448.   if(w->rich_text.invert)
  449.     XtReleaseGC((Widget)w, w->rich_text.invert);
  450.  
  451.   values.function = GXxor;
  452.   values.foreground = w->primitive.foreground ^ w->core.background_pixel;
  453.   w->rich_text.invert = XtGetGC((Widget)w, valuemask, &values);
  454. }
  455.  
  456. static int
  457. InvertSelection(XcRichTextWidget w)
  458. {
  459.   int* x = w->rich_text.sx;
  460.   int* y = w->rich_text.sy;
  461.  
  462.   if(w->rich_text.point != w->rich_text.mark){
  463.     if(y[3] > y[1]){
  464.       /* case 1:
  465.        * "score ... our" is selected:
  466.        *
  467.        * x0      x1   x2             x3
  468.        * |       |    |              |_____y0
  469.        * |  Four score and seven years_____y1,y2
  470.        * ago today, our forefathers  |_____y3
  471.        * brought forth,
  472.        */
  473.       XFillRectangle(XtDisplay((Widget)w), XtWindow((Widget)w),
  474.              w->rich_text.invert,
  475.              x[1], y[0], x[3]-x[1], y[1]-y[0]);
  476.       XFillRectangle(XtDisplay((Widget)w), XtWindow((Widget)w),
  477.              w->rich_text.invert,
  478.              x[0], y[2], x[2]-x[0], y[3]-y[2]);
  479.       if(y[2] > y[1])
  480.     /* case 3:
  481.      * "score ... brought" is selected:
  482.      *
  483.      * x0      x1
  484.      * |       |                    _____y0
  485.      * |  Four score and seven years_____y1
  486.      * ago today, our forefathers  |_____y2
  487.      * brought forth,              |_____y3
  488.      *       |                     |
  489.      *       x2                   x3
  490.      */
  491.     XFillRectangle(XtDisplay((Widget)w), XtWindow((Widget)w),
  492.                w->rich_text.invert,
  493.                x[0], y[1], x[3]-x[0], y[2]-y[1]);
  494.     }else{
  495.       /*
  496.        * case 3:
  497.        *  "score and seven"
  498.        * x0      x1           x2     x3
  499.        * |       |             |     |_____y0, y2
  500.        * |  Four score and seven years_____y1, y3
  501.        * ago today, our forefathers
  502.        * brought forth,
  503.        */
  504.       XFillRectangle(XtDisplay((Widget)w), XtWindow((Widget)w),
  505.              w->rich_text.invert,
  506.              x[1], y[0], x[2]-x[1], y[3]-y[0]);
  507.     }
  508.   }
  509. }
  510.  
  511.       
  512.  
  513. static void
  514. FillDifference(Display* disp,
  515.            Drawable d,
  516.            GC gc,
  517.            int x1, int y1, unsigned w1, unsigned h1,
  518.            int x2, int y2, unsigned w2, unsigned h2)
  519. {
  520.   int xx1 = x1+w1;
  521.   int yy1 = y1+h1;
  522.   int xx2 = x2+w2;
  523.   int yy2 = y2+h2;
  524.  
  525.   if(yy2<y1 || x2>xx2 || y2>yy1 || xx2<x1){
  526.     /* no intersection... */
  527.     XFillRectangle(disp, d, gc,
  528.            x1, y1, w1, h1);
  529.   } else
  530.     if(x2 <= x1 && xx2 >= xx1){
  531.       /* case 1: 0 horiz. intervals */
  532.       if(y2 <= y1 && yy2 >= yy1){
  533.     /* case 1a: 0 vert. intervals */
  534.       }else if (y2 > y1 && yy2 < yy1){
  535.     /* case 2b: 2 vert intervals */
  536.     XFillRectangle(disp, d, gc,
  537.                x1, y1, w1, y2-y1);
  538.     XFillRectangle(disp, d, gc,
  539.              x1, yy2, w1, yy1-yy2);
  540.       }else{
  541.     /* case 1c: 1 vert interval */
  542.     int y,h;
  543.     
  544.     if(y2 > y1)
  545.       y = y1, h = y2-y1;
  546.     else
  547.       y = yy2, h = yy1-yy2;
  548.     XFillRectangle(disp, d, gc,
  549.                x1, y, w1, h);
  550.       }
  551.     }else if (x2 > x1 && xx2 < xx1){
  552.       /* case 2: 2 horiz intervals */
  553.       XFillRectangle(disp, d, gc,
  554.              x1, y1, x2-x1, h1);
  555.       XFillRectangle(disp, d, gc,
  556.              xx2, y1, xx1-xx2, h1);
  557.       
  558.       if(y2 <= y1 && yy2 >= yy1){
  559.     /* case 2a: 0 vert. intervals */
  560.       }else if (y2 >y1 && yy2 < yy1){
  561.     /* case 2b: 2 vert intervals */
  562.     XFillRectangle(disp, d, gc,
  563.                x2, y1, w2, y2-y1);
  564.     XFillRectangle(disp, d, gc,
  565.                x2, yy2, w2, yy1-yy2);
  566.       }else{
  567.     /* case 2c: 1 vert interval */
  568.     int y,h;
  569.     
  570.     if(y2 > y1)
  571.       y = y1, h = y2-y1;
  572.     else
  573.       y = yy2, h = yy1-yy2;
  574.     XFillRectangle(disp, d, gc,
  575.                x2, y, w2, h);
  576.       }
  577.     }else{
  578.       /* case 3: 1 horiz interval */
  579.       int x,w, xy, wy;
  580.       
  581.       if(x2 > x1){
  582.     x = x1;
  583.     w = x2-x1;
  584.     xy = xx2;
  585.     wy = xx1-xx2;
  586.       }else{
  587.     x = xx2;
  588.     w = xx1-xx2;
  589.     xy = x1;
  590.     wy = x2-x1;
  591.       }
  592.       XFillRectangle(disp, d, gc,
  593.              x, y1, w, h1);
  594.       
  595.       if(y2 <= y1 && yy2 >= yy1){
  596.     /* case 3a: 0 vert. intervals */
  597.       }else if (y2 >y1 && yy2 < yy1){
  598.     /* case 3b: 2 vert intervals */
  599.     XFillRectangle(disp, d, gc,
  600.                xy, y1, wy, y2-y1);
  601.     XFillRectangle(disp, d, gc,
  602.                xy, yy2, wy, yy1-yy2);
  603.       }else{
  604.     /* case 1c: 1 vert interval */
  605.     int y,h;
  606.     
  607.     if(y2 > y1)
  608.       y = y1, h = y2-y1;
  609.     else
  610.       y = yy2, h = yy1-yy2;
  611.     XFillRectangle(disp, d, gc,
  612.                xy, y, wy, h);
  613.       }
  614.     }
  615. }
  616.  
  617.  
  618.            
  619. static int
  620. InvertDifference(XcRichTextWidget w,
  621.          int rx, int ry,
  622.          unsigned rw, unsigned rh)
  623. {
  624.   int* x = w->rich_text.sx;
  625.   int* y = w->rich_text.sy;
  626.  
  627.   if(w->rich_text.mark != w->rich_text.point){
  628.     if(y[3] > y[1]){
  629.       FillDifference(XtDisplay((Widget)w), XtWindow((Widget)w),
  630.              w->rich_text.invert,
  631.              x[1], y[0], x[3]-x[1], y[1]-y[0],
  632.              rx, ry, rw, rh);
  633.       FillDifference(XtDisplay((Widget)w), XtWindow((Widget)w),
  634.              w->rich_text.invert,
  635.              x[0], y[2], x[2]-x[0], y[3]-y[2],
  636.              rx, ry, rw, rh);
  637.       if(y[2] > y[1])
  638.     FillDifference(XtDisplay((Widget)w), XtWindow((Widget)w),
  639.                w->rich_text.invert,
  640.                x[0], y[1], x[3]-x[0], y[2]-y[1],
  641.              rx, ry, rw, rh);
  642.     }else{
  643.       FillDifference(XtDisplay((Widget)w), XtWindow((Widget)w),
  644.              w->rich_text.invert,
  645.              x[1], y[0], x[2]-x[1], y[3]-y[0],
  646.              rx, ry, rw, rh);
  647.     }
  648.   }
  649. }
  650.  
  651.       
  652.  
  653. static void
  654. SelectStart(Widget w, XEvent* ev, String* argv, Cardinal* argc)
  655. {
  656.   XcRichTextWidget rt = (XcRichTextWidget)w;
  657.  
  658.   XcRichTextPart* p = &rt->rich_text;
  659.  
  660.   if(p->point != p->mark){
  661.     debug((" unselect old: \"%.*s\"\n",
  662.        p->point - p->mark,
  663.        p->value + p->mark));
  664.   
  665.     InvertSelection(rt);
  666.   }
  667.  
  668.   p->mark =
  669.     p->point = XTextView_position(p->flow,
  670.                  ev->xbutton.x,
  671.                  ev->xbutton.y);
  672.  
  673.   debug(("set start = %ld (%c)\n",
  674.      p->point,
  675.      *(p->value + p->point)));
  676.   PrepareInvertGC(rt);
  677. }
  678.  
  679. static void UpdateSelection(XcRichTextWidget rt, long h)
  680. {
  681.   long pt = rt->rich_text.point;
  682.   long s, e;
  683.  
  684.   if(pt == h)
  685.     return;
  686.  
  687.   if(h < pt){
  688.     s = h;
  689.     e = pt;
  690.   }else{
  691.     s = pt;
  692.     e = h;
  693.   }
  694.  
  695.   debug(("invert [%.*s]\n",
  696.      e-s,
  697.      rt->rich_text.value + s));
  698.   XTextView_region(rt->rich_text.flow, s, e,
  699.            rt->rich_text.sx,
  700.            rt->rich_text.sy);
  701.   rt->rich_text.point = h;
  702.   InvertSelection(rt);
  703. }
  704.  
  705. static void
  706. ExtendStart(Widget w, XEvent* ev, String* argv, Cardinal* argc)
  707. {
  708.   XcRichTextWidget rt = (XcRichTextWidget)w;
  709.   XcRichTextPart* p = &rt->rich_text;
  710.  
  711.   long h = XTextView_position(p->flow,
  712.                   ev->xbutton.x,
  713.                   ev->xbutton.y);
  714.  
  715.   /* ASSUME: mark <= point */
  716.   if(abs(h - p->mark) <
  717.      abs(h - p->point)){
  718.     long tmp = p->mark;
  719.     p->mark = p->point;
  720.     p->point = tmp;
  721.   }
  722.  
  723.   PrepareInvertGC(rt);
  724.   UpdateSelection(rt, h);
  725. }
  726.  
  727.  
  728. static void
  729. ExtendAdjust(Widget w, XEvent* ev, String* argv, Cardinal* argc)
  730. {
  731.   XcRichTextWidget rt = (XcRichTextWidget)w;
  732.   XcRichTextPart* p = &rt->rich_text;
  733.  
  734.   long h = XTextView_position(p->flow,
  735.                   ev->xbutton.x,
  736.                   ev->xbutton.y);
  737.  
  738.   UpdateSelection(rt, h);
  739. }
  740.  
  741.  
  742. static Boolean
  743.   ConvertSelection(Widget w,
  744.            Atom* selection,
  745.            Atom* target,
  746.            Atom* type,
  747.            XtPointer *value,
  748.            unsigned long *length,
  749.            int* format)
  750. {
  751.   XcRichTextWidget rt = (XcRichTextWidget)w;
  752.   XcRichTextPart* p = &rt->rich_text;
  753.   
  754.   if(*target == XmInternAtom(XtDisplay(w), "TARGETS", 1)){
  755.     *type = XA_ATOM;
  756.     *value = (XtPointer)XtNew(Atom);
  757.     *((Atom*)(*value)) = XA_STRING;
  758.     *length = 2;
  759.     *format = 32;
  760.     return TRUE;
  761.   }else if(*target == XA_STRING){
  762.     long l = p->point - p->mark;
  763.     *type = XA_STRING;
  764.     *value = XTextView_plain_text(p->value + p->mark, l);
  765.     *length = strlen(*value);
  766.     *format = 8;
  767.     return TRUE;
  768.   }
  769.   return FALSE;
  770. }
  771.  
  772. static void
  773. LoseSelection(Widget w, Atom* selection)
  774. {
  775.   XcRichTextWidget rt = (XcRichTextWidget)w;
  776.   XcRichTextPart* p = &rt->rich_text;
  777.  
  778.   if(p->point == p->mark)
  779.     return;
  780.  
  781.   debug(("lose selection: [%.*s]\n",
  782.      p->point - p->mark,
  783.      p->value + p->mark));
  784.   InvertSelection(rt);
  785.   p->point = p->mark;
  786. }
  787.  
  788. static void
  789. ExtendEnd(Widget w, XEvent* ev, String* argv, Cardinal *argc)
  790. {
  791.   XcRichTextWidget rt = (XcRichTextWidget)w;
  792.   XcRichTextPart* p = &rt->rich_text;
  793.  
  794.   long h = XTextView_position(p->flow,
  795.                   ev->xbutton.x,
  796.                   ev->xbutton.y);
  797.  
  798.   UpdateSelection(rt, h);
  799.  
  800.   if(p->point < p->mark){
  801.     long tmp = p->mark;
  802.     p->mark = p->point;
  803.     p->point = tmp;
  804.   }
  805.  
  806.   if(p->point > p->mark)
  807.     XTextView_region(p->flow, p->mark, p->point,
  808.              p->sx,
  809.              p->sy);
  810.  
  811.   debug(("selection: [%.*s]\n",
  812.      p->point - p->mark,
  813.      p->value + p->mark));
  814.  
  815.   {
  816.     Cardinal i;
  817.     for(i = 0; i<*argc; i++){
  818.       Atom selection = XmInternAtom(XtDisplay(w), argv[i], 0);
  819.       if(selection)
  820.     if(!XtOwnSelection(w, selection, ev->xbutton.time,
  821.                ConvertSelection, LoseSelection, NULL))
  822.       debug(("can't get selection: %s\n", argv[i]));
  823.     }
  824.   }
  825. }
  826.  
  827. /* XTextView support */
  828.  
  829. XFontStruct*
  830. Widget_font(void* w, const char* charset)
  831. {
  832.   XcRichTextWidget rw = (XcRichTextWidget)w;
  833.   short indx;
  834.   XFontStruct* font;
  835.  
  836.   if(!_XmFontListSearch(rw->rich_text.fontList,
  837.             (XmStringCharSet)charset,
  838.             &indx,
  839.             &font))
  840.     _XmFontListGetDefaultFont(rw->rich_text.fontList,
  841.                  &font);
  842.  
  843.   return font;
  844. }
  845.  
  846. unsigned long
  847. Widget_color(void* w, int r, int g, int b)
  848. {
  849.   Colormap cm;
  850.   XcRichTextWidget rw = (XcRichTextWidget)w;
  851.  
  852.   XColor c;
  853.   c.red = r;
  854.   c.green = g;
  855.   c.blue = b;
  856.   XtVaGetValues((Widget)rw,
  857.         XmNcolormap, &cm,
  858.         0);
  859.   if(XAllocColor(XtDisplay(rw), cm, &c))
  860.     return c.pixel;
  861.   else
  862.     return rw->primitive.foreground;
  863. }
  864.  
  865. GC
  866. Widget_get_gc(void* w, unsigned long mask, XGCValues* v)
  867. {
  868.   return XtGetGC((Widget)w, mask, v);
  869. }
  870.  
  871. void
  872. Widget_free_gc(void* w, GC gc)
  873. {
  874.   XtReleaseGC((Widget)w, gc);
  875. }
  876.  
  877.  
  878. /* text functions */
  879. char *XcRichTextGetString(Widget widget)
  880. {
  881.   XcRichTextWidget r = (XcRichTextWidget)widget;
  882.  
  883.   return XtNewString(r->rich_text.value);
  884. }
  885.  
  886. long XcRichTextGetLastPosition (Widget widget)
  887. {
  888.   XcRichTextWidget r = (XcRichTextWidget)widget;
  889.  
  890.   return r->rich_text.bytes;
  891. }
  892.  
  893. char *XcRichTextGetSelection (Widget widget, int strip_rtf)
  894. {
  895.   XcRichTextWidget r = (XcRichTextWidget)widget;
  896.   long l = r->rich_text.point - r->rich_text.mark;
  897.  
  898.   if(l == 0)
  899.     return 0;
  900.  
  901.   if(strip_rtf)
  902.     return XTextView_plain_text(r->rich_text.value + r->rich_text.mark, l);
  903.   else{
  904.     char* ret = XtMalloc(l+1);
  905.     strncpy(ret, r->rich_text.value + r->rich_text.mark, l);
  906.     ret[l] = 0;
  907.     return ret;
  908.   }
  909. }
  910.  
  911. void XcRichTextSetSelection (Widget widget,
  912.                  long first,
  913.                  long last,
  914.                  Time set_time)
  915. {
  916.   XcRichTextWidget r = (XcRichTextWidget)widget;
  917.   Atom selection = XmInternAtom(XtDisplay(widget), "PRIMARY", 0); /* @@ */
  918.  
  919.   LoseSelection(widget, 0);
  920.  
  921.   if(first > r->rich_text.bytes || last > r->rich_text.bytes)
  922.     return;
  923.  
  924.   r->rich_text.mark = r->rich_text.point = first;
  925.   UpdateSelection(r, last);
  926.   if(selection)
  927.     XtOwnSelection(widget, selection, set_time,
  928.            ConvertSelection, LoseSelection, NULL);
  929. }
  930.  
  931. void XcRichTextClearSelection (Widget widget, Time clear_time)
  932. {
  933.   LoseSelection(widget, 0);
  934. }
  935.  
  936. Boolean XcRichTextGetSelectionPosition (Widget widget,
  937.                     long *left, 
  938.                     long *right)
  939. {
  940.   XcRichTextWidget r = (XcRichTextWidget)widget;
  941.   if(r->rich_text.point == r->rich_text.mark)
  942.     return 0;
  943.   *left = r->rich_text.mark;
  944.   *right = r->rich_text.mark;
  945.   return 1;
  946. }
  947.  
  948. long XcRichTextXYToPos (Widget widget,
  949.             Position x, Position y)
  950. {
  951.   XcRichTextWidget r = (XcRichTextWidget)widget;
  952.   return XTextView_position(r->rich_text.flow, x, y);
  953. }
  954.  
  955. Boolean XcRichTextPosToXY (Widget widget,
  956.                long pos, Position *x, Position *y)
  957. {
  958.   XcRichTextWidget r = (XcRichTextWidget)widget;
  959.   if(pos >= 0 && pos <= r->rich_text.bytes){
  960.     int yy;
  961.     *x = XTextView_locate(r->rich_text.flow, pos, &yy, 0);
  962.     *y = yy;
  963.     return 1;
  964.   }else
  965.     return 0;
  966. }
  967.